import java.awt.Color;
import java.util.LinkedList;
import java.util.concurrent.LinkedBlockingQueue;


/**
 * implementatie van een lock volgens het algoritme van
 * Ricart en Agrawala (Distributed Systems p.475)
 * @author Tom Vereenooghe & Jelle Polders
 *
 */
public class Lock extends Thread {
	
	public enum Status {
		RELEASED,
		WANTED,
		HELD
	}
	
	public Status status;
	public LinkedBlockingQueue<Message> queuedRequests;
	
	private SpelManager spelManager;
	private VectorClock ownRequestVectorClock; // om te vergelijken of jijzelf of een ander proces eerst was met een request
	private int repliesReceived;
	protected LinkedBlockingQueue<Message> ownRequestQueue;
	
	/**
	 * Constructor
	 */
	public Lock(SpelManager spelManager) {
		status = Status.RELEASED;
		queuedRequests = new LinkedBlockingQueue<Message>();
		this.spelManager = spelManager;
		ownRequestQueue = new LinkedBlockingQueue<Message>();		
	}
	
	public void add(Message msg) {
		ownRequestQueue.offer(msg);
	}
	
	public void run() {
		while (ownRequestQueue.size() > 0) {
			Message msg = ownRequestQueue.poll();
			status = Status.WANTED;
			// multicast request naar alle processen;
			ownRequestVectorClock = spelManager.sendRequest();

			// wachten tot je N-1 replies ontvangen hebt
			repliesReceived = 0;
		
			while (repliesReceived < spelManager.getAantalSpelers() - 1)
			{
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		
			status = Status.HELD;
			spelManager.sendMove(msg);
			status = Status.RELEASED;
		}
	}
	
	public void replyReceived() {
		repliesReceived++;
	}
	
	
	
	public synchronized void onRequestReceived(Message msg) {
		if (status == Status.HELD)
		{
			//System.out.println("HELD");
			queuedRequests.offer(msg);
		}
		else if (status == Status.WANTED)
		{
			//System.out.println("WANTED");
			if (ownRequestVectorClock.compareTo(msg.getVectorClock()) < 0)
				queuedRequests.add(msg);
			else if (ownRequestVectorClock.compareTo(msg.getVectorClock()) == 0 && spelManager.eigenID < msg.zenderID)
				queuedRequests.add(msg);
			else
				spelManager.sendReply(msg.zenderID);
		}
		else
		{
			//System.out.println("RELEASED");
			spelManager.sendReply(msg.zenderID);
		}
	}
	
	public synchronized void exitCriticalSection() {
		status = Status.RELEASED;
		Message msg = null;
		
		msg = queuedRequests.poll();
		while (msg != null)
		{
			spelManager.sendReply(msg.zenderID);
			msg = queuedRequests.poll();
		}
	}
}
